/*
* Copyright (C) 2021, KylinSoft Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/&gt;.
*
*/

#include "mainwidget.h"
#include <QMessageBox>
#include <QWidgetList>
#include <QPropertyAnimation>
#include <QTimer>
#include <kylin_system/window_management.hpp>
#include <gsettings.hpp>
#include <kylin_system/user_manual.hpp>
#include <KWindowEffects>
#include "kabase/Qt/windowmanage.hpp"



static bool isExited = false; //! exit scan thread

int const MainWidget::EXIT_CODE_REBOOT = -123456789;

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent),
      m_titleBar(new TitleBar()),
      m_displayWidget(new DisplayWidget()),
//      m_displayScrollArea(new QScrollArea),
      m_mainWidgetVLayout(new QVBoxLayout())
{
    kabase::WindowManage::getWindowId(&windowId);
    setupGui();
    initConnect();
    initGsettings();


    if (g_config_signal->m_kylinScannerImageDebug) {
        scanThreadFinishedSlot(SANE_STATUS_GOOD);
    } else {
        m_detectScanDevicesThread.start();
        m_usbHotplugThread.start();
    }
}

MainWidget::~MainWidget()
{
    g_sane_object->stopSaneRead(true);

    exit(0);

}

void MainWidget::setupGui()
{
    // Frosted glass effect, must before than XAtomHelper
    this->setProperty("useSystemStyleBlur", true);

    this->setAttribute(Qt::WA_TranslucentBackground, true);

    // Add window control protocol
    ::kabase::WindowManage::removeHeader(this);

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    resize(MainWidgetWidth, MainWidgetHeight);
    setMinimumSize(MainWidgetWidth, MainWidgetHeight);
    setWindowTitle(tr("kylin-scanner"));
//    this->setWindowIcon(QIcon::fromTheme("kylin-scanner"));
    setMouseTracking(true);
    setAutoFillBackground(true);
    setAcceptDrops(true);

    // Center window
    QScreen *screen = QGuiApplication::primaryScreen();
    move((screen->geometry().width() - MainWidgetWidth) / 2, (screen->geometry().height() - MainWidgetHeight) / 2);

    this->setObjectName("MainWindow");

//    m_displayScrollArea->setWidget(m_displayWidget);
//    m_displayScrollArea->setWidgetResizable(true);
//    m_displayScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//    m_displayScrollArea->setFrameShape(QFrame::NoFrame);
//    m_displayScrollArea->setMinimumWidth(MainWidgetWidth);
//    m_displayWidget->adjustSize();


    m_mainWidgetVLayout->setSpacing(0);
    m_mainWidgetVLayout->addWidget(m_titleBar);
//    m_mainWidgetVLayout->addWidget(m_displayScrollArea);
    m_mainWidgetVLayout->addWidget(m_displayWidget);
    m_mainWidgetVLayout->setContentsMargins(0, 0, 0, 0);
    setLayout(m_mainWidgetVLayout);

}

void MainWidget::initConnect()
{
    connect(g_user_signal, &GlobalUserSignal::minimumWindowSignal, this, &MainWidget::showMinimized);
    connect(g_user_signal, &GlobalUserSignal::maximumWindowSignal, this, &MainWidget::maximizeWindowSlot);
    connect(g_user_signal, &GlobalUserSignal::exitApplicationSignal, this, &MainWidget::closeWindowSlot);
    connect(g_user_signal, &GlobalUserSignal::showAboutDialogSignal, this, &MainWidget::showAboutWindowSlot);

    connect(m_displayWidget, &DisplayWidget::detectScanDevicesSignal, this, &MainWidget::detectScanDevicesSlot, Qt::QueuedConnection);

    connect(&m_detectScanDevicesThread, &DetectScanDevicesThread::detectScanDevicesFinishedSignal, this, &MainWidget::detectScanDeviceThreadFinishedSlot);

    connect(&m_usbHotplugThread, &UsbHotplugThread::usbAdd, this, &MainWidget::usbAddedOperationSlot);
    connect(&m_usbHotplugThread, &UsbHotplugThread::usbRemove, this, &MainWidget::usbRemovedOperationSlot);

    connect(g_user_signal, &GlobalUserSignal::startScanOperationSignal, this, &MainWidget::startScanOperationSlot, Qt::QueuedConnection);
    connect(g_user_signal, &GlobalUserSignal::stopScanOperationSignal, this, &MainWidget::stopScanOperationSlot);

    connect(g_user_signal, &GlobalUserSignal::scanThreadFinishedSignal, this, &MainWidget::scanThreadFinishedSlot);

    connect(g_user_signal, &GlobalUserSignal::toolbarBeautyOperationStartSignal, this, &MainWidget::startBeautyOperationSlot);
//    connect(g_user_signal, &GlobalUserSignal::doBeautyOperationSignal, this, &MainWidget::startBeautyOperationSlot);
    connect(g_user_signal, &GlobalUserSignal::doBeautyOperationFinishedSignal, this, &MainWidget::hideBeautyRunningDialog);

    connect(g_user_signal, &GlobalUserSignal::toolbarRectifyOperationStartSignal, this, &MainWidget::showRectifyRunningDialog);
    connect(g_user_signal, &GlobalUserSignal::doRectifyOperationSignal, this, &MainWidget::startRectifyOperationSlot);
    connect(g_user_signal, &GlobalUserSignal::doRectifyOperationFinishedSignal, this, &MainWidget::hideRectifyRunningDialog);

    connect(g_user_signal, &GlobalUserSignal::doOcrOperationSignal, this, &MainWidget::startOcrOperationSlot);

    connect(g_user_signal, &GlobalUserSignal::dbusInhabitSignal, this, &MainWidget::dbusInhabitSlot);
    connect(g_user_signal, &GlobalUserSignal::dbusUnInhabitSignal, this, &MainWidget::dbusUnInhabitSlot);
    connect(g_user_signal, &GlobalUserSignal::cancelScanning, this, &MainWidget::cancelScanningSlot);

}

void MainWidget::initGsettings()
{   
    themeChange();
    transparencyChange();
    iconThemeNameChange();
    fontSizeChange();
    connect(kdk::kabase::Gsettings::getPoint(), &kdk::kabase::Gsettings::systemThemeChange, this, &MainWidget::themeChange);
    connect(kdk::kabase::Gsettings::getPoint(), &kdk::kabase::Gsettings::systemThemeChange, this, &MainWidget::iconThemeNameChange);
    connect(kdk::kabase::Gsettings::getPoint(), &kdk::kabase::Gsettings::systemFontSizeChange, this, &MainWidget::fontSizeChange);
    connect(kdk::kabase::Gsettings::getPoint(), &kdk::kabase::Gsettings::systemTransparencyChange, this, &MainWidget::transparencyChange);
    return;
}

bool MainWidget::isDarkTheme()
{
    QString systemTheme = kdk::kabase::Gsettings::getSystemTheme().toString();

    if (systemTheme == STYLE_NAME_KEY_DARK || systemTheme == STYLE_NAME_KEY_BLACK) {
        return true;
    } else {
        return false;
    }
}

/**
 * @brief MainWidget::resizeTitleBar
 * resize Titlebar while MainWidget size have been resized
 */
void MainWidget::resizeTitleBar()
{
    m_titleBar->move(0, 0);
    m_titleBar->resize(this->width(), TitlebarHeight);
}

void MainWidget::resizeDisplayWidget()
{
    m_displayWidget->resize(this->width(), this->height() - TitlebarHeight);
}

void MainWidget::themeChange()
{
    QPalette pal(palette());
    if (isDarkTheme()) {
        pal.setColor(QPalette::Background, QColor(64, 64, 64));
    } else {
        pal.setColor(QPalette::Background, QColor(240, 240, 240));
    }
    setAutoFillBackground(true);
    setPalette(pal);
}

void MainWidget::iconThemeNameChange()
{

//    this->setWindowIcon(QIcon::fromTheme("kylin-scanner"));

}

void MainWidget::fontSizeChange()
{    
    int fontSize = kdk::kabase::Gsettings::getSystemFontSize().toInt();
    KyInfo() << "fontSize = " << fontSize;
}

void MainWidget::transparencyChange()
{
    double transparencyValue = kdk::kabase::Gsettings::getSystemTransparency().toDouble();
    g_config_signal->m_transparency = transparencyValue * 255;
    this->update();
}
void MainWidget::warnMsg(QString msg)
{
    QMessageBox *msgBox = new QMessageBox();
    if (isDarkTheme()) {
        QPalette pal(palette());
        pal.setColor(QPalette::Background, QColor(0, 0, 0));
        msgBox->setAutoFillBackground(true);
        msgBox->setPalette(pal);
    } else {
        QPalette pal(palette());
        pal.setColor(QPalette::Background, QColor(255, 255, 255));
        msgBox->setAutoFillBackground(true);
        msgBox->setPalette(pal);
    }
    msgBox->setAttribute(Qt::WA_ShowModal, true);

    msgBox->setText(msg);
    msgBox->setIcon(QMessageBox::Warning);
//    msgBox->setWindowIcon(QIcon::fromTheme("kylin-scanner"));
    msgBox->setWindowTitle(tr("Scanner"));
    msgBox->setStandardButtons(QMessageBox::Yes);
    msgBox->setContextMenuPolicy(Qt::NoContextMenu);
    msgBox->button(QMessageBox::Yes)->setText(tr("Yes"));

    QWidget *widget = nullptr;
    QWidgetList widgetList = QApplication::allWidgets();
    for (int i=0; i<widgetList.length(); ++i) {
        if (widgetList.at(i)->objectName() == "MainWindow") {
            widget = widgetList.at(i);
        }
    }
    if (widget) {
        msgBox->setParent(widget);
        QRect rect = widget->geometry();
        int x = rect.x() + rect.width()/2 - msgBox->width()/2;
        int y = rect.y() + rect.height()/2 - msgBox->height()/2;
        msgBox->move(x,y);
    }

    QTimer* timer = new QTimer();
    timer->start(3000);
    timer->setSingleShot(true);

    connect(timer, &QTimer::timeout, msgBox, &QMessageBox::accept);
    connect(kdk::kabase::Gsettings::getPoint(), &kdk::kabase::Gsettings::systemThemeChange, this, [=]{
        if (isDarkTheme()) {
            QPalette pal(palette());
            pal.setColor(QPalette::Background, QColor(0, 0, 0));
            msgBox->setAutoFillBackground(true);
            msgBox->setPalette(pal);
        } else {
            QPalette pal(palette());
            pal.setColor(QPalette::Background, QColor(255, 255, 255));
            msgBox->setAutoFillBackground(true);
            msgBox->setPalette(pal);
        }
    });

    msgBox->exec();
}


void MainWidget::reboot()
{
    qApp->exit(EXIT_CODE_REBOOT);
}

void MainWidget::resizeEvent(QResizeEvent *event)
{

    if (this->isFullScreen() || this->isMaximized()) {
        m_titleBar->updateMaxButtonStatus(true);
    } else {
        m_titleBar->updateMaxButtonStatus(false);
    }

    if (this->height() < MainWidgetHeight || this->width() < MainWidgetWidth) {
//        MainWidgetHeight = this->height();
//        MainWidgetWidth = this->width();
//        resize(MainWidgetWidth, MainWidgetHeight);
//        setMinimumSize(MainWidgetWidth, MainWidgetHeight);
    }

    QWidget::resizeEvent(event);
}

void MainWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    /* 反锯齿 */
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);

    QPainterPath rectPath;
    rectPath.addRoundedRect(this->rect(), 0, 0);
    /* 开启背景模糊效果（毛玻璃） */
    KWindowEffects::enableBlurBehind(this->winId(), true, QRegion(rectPath.toFillPolygon().toPolygon()));

    QStyleOption opt;
    opt.init(this);

    p.setPen(Qt::NoPen);
    QColor color = palette().color(QPalette::Base);
    color.setAlpha(g_config_signal->m_transparency);
    QPalette pal(this->palette());
    pal.setColor(QPalette::Window,QColor(color));
    this->setPalette(pal);
    QBrush brush =QBrush(color);
    p.setBrush(brush);
    p.drawRoundedRect(opt.rect,0,0);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    p.fillPath(rectPath, brush);
    return;
}

void MainWidget::keyReleaseEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_F1:
        KyInfo() << "F1 ==========";
        showHelpDialog();
        break;
    default:
        QWidget::keyReleaseEvent(event);
        break;
    }
    return QWidget::keyReleaseEvent(event);
}
void MainWidget::showHelpDialog()
{
    kdk::kabase::UserManualManagement userManualTest;
    if (!userManualTest.callUserManual(kdk::kabase::AppName::KylinScanner)) {
        qCritical() << "user manual call fail!";
    }
    return;
}
void MainWidget::dbusInhabitSlot(){

    flag = sessionManagementTest.setInhibitLockScreen(kdk::kabase::AppName::KylinFontViewer , QString("TestReason"));
    if (flag == 0) {
        qCritical() << "set inhibit lock screen fail";
        return;
    }
    qDebug()<<"test result:inhabit";
}
void MainWidget::dbusUnInhabitSlot(){
    /* 取消禁止锁屏 */
    sessionManagementTest.unInhibitLockScreen(flag);
    qDebug()<<"test result:un_inhabit";
}

void MainWidget::maximizeWindowSlot()
{
    if (this->isFullScreen()) {
        m_titleBar->updateMaxButtonStatus(false);
        this->showNormal();
    } else {
        if (this->isMaximized()) {
            m_titleBar->updateMaxButtonStatus(false);
            this->showNormal();
        } else {
            m_titleBar->updateMaxButtonStatus(true);
            this->showMaximized();
        }
    }
}
quint32 MainWidget::getWindowId(){
    return windowId;
}
void MainWidget::closeWindowSlot()
{
    exit(0);
}

void MainWidget::showAboutWindowSlot()
{
    m_aboutDialog = new AboutDialog();

    QPoint globalPos = this->mapToGlobal(QPoint(0, 0));
    m_aboutDialog->move(globalPos.x() + (this->width() - m_aboutDialog->width())/2,
                        globalPos.y() + (this->height() - m_aboutDialog->height())/2);

    m_aboutDialog->show();
}

void MainWidget::styleSettingsChanged(QString)
{
    QPalette pal(palette());
    if (isDarkTheme()) {
        pal.setColor(QPalette::Background, QColor(38, 38, 38));
    } else {
        pal.setColor(QPalette::Background, QColor(255, 255, 255));
    }
    setAutoFillBackground(true);
    setPalette(pal);
}

void MainWidget::detectScanDevicesSlot()
{
    if (! m_detectScanDevicesThread.isRunning()) {
        KyInfo() << "Not running detect scan devices thread. Let's detect ...";
        m_detectScanDevicesThread.start();

        g_user_signal->detectPageWaitTimerStart();
    }
}

void MainWidget::detectScanDeviceThreadFinishedSlot(bool isDetected)
{
    g_user_signal->detectPageWaitTimerStop();

    if (isDetected) {
        // while detect scan device thread finished, we should open the first device to get scan parameters
        g_sane_object->openSaneDeviceForPage(0);

        m_displayWidget->showSuccessPageSlot(isDetected);
    } else {
        m_displayWidget->showFailedPageSlot();
    }

}

static QString getCurrentScanDevices()
{
//    QString cmd(BashType);
    QStringList arglists;

//    arglists << "-c";
//    arglists << "scanimage";
    arglists << "-L";

    QProcess *process = new QProcess();
    process->start("scanimage", arglists);
    process->waitForFinished();
    QString devices = QString::fromLocal8Bit(process->readAllStandardOutput());

    KyInfo() << "current Scan devices:  " << devices;

    return devices;
}

static int getSubStringNumber(QString src, QString sub)
{
    QStringList srclist = src.split(sub);
    KyInfo() << "srclist = " << srclist
             << "srclist.size = " << srclist.size();

    return srclist.size()-1;
}

void MainWidget::usbAddedOperationSlot(QString recvData)
{
    KyInfo() << "USB Add: " << recvData;

    QProcess *scanList = new QProcess(this);
    QStringList  argvList;
    argvList.append("-L");
    scanList->start("scanimage", argvList);

    connect(scanList, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
    [ = ](int exitCode, QProcess::ExitStatus exitStatus) {

        KyInfo() << "USB Add exitCode = " << exitCode
                 << "exitStatus = " << exitStatus;

//        m_detectScanDevicesThread.start();
        if (0 == exitCode) {
            QString result = QString::fromLocal8Bit(scanList->readAll());
            KyInfo() << "result = " << result;

            // firstly 'scanimage -L' will failed, so need call it again.
            QString devices = getCurrentScanDevices();

            QStringList strListDevice;
            strListDevice =g_sane_object->getSaneNames();
            KyInfo() << "current sane names: " << strListDevice
                     << "userInfo.name= " << g_sane_object->userInfo.name
                     << "size = " << strListDevice.size();

            if (strListDevice.size() == 0 || strListDevice.isEmpty()) {
                if ((! devices.isEmpty())
                        && (! devices.contains("No scanners were identified", Qt::CaseSensitive))) {
                    KyInfo() << "Found scan devices:  " << devices;
                    if (! m_detectScanDevicesThread.isRunning()) {
                        KyInfo() << "begin detect scan devices.";
                        g_sane_object->saneExit();
                        m_detectScanDevicesThread.start();
                        QString msg = tr("There is a new scanner connect, redetect all scanners, please wait a moment. ");
                        warnMsg(msg);

                        return;
                    }
                }
            }

            int count = 0;
            count = getSubStringNumber(devices, "is a");
            KyInfo() << "Fount scanner size: " << count
                     << "current scanner size: " << strListDevice.size();

            if (count > strListDevice.size()) {
                if (! m_detectScanDevicesThread.isRunning()) {
                    g_sane_object->saneExit();
                    m_detectScanDevicesThread.start();

                }

                KyInfo() << "device add";
                QString msg = tr("There is a new scanner connect, redetect all scanners, please wait a moment. ");
                warnMsg(msg);

                return;

            }

            for (int i = 0; i < strListDevice.size(); ++i) {
                QString str = strListDevice.at(i).toLocal8Bit().constData();
                KyInfo() << "i=" << i << "str = " << str;

                if (str == tr("No available device")) {
                    break;
                }

                /// There are two cases that we cannot find scanners throught `scanimage -L`:
                /// case 1. no this scanner in system, which means this scanner has been disconnect
                /// case 2. this scanner has been connect in kylin-scanner by sane_init
                /// case 3. this scanner has been disconnet, but net or usb can be found CaseInsensitive, such as hp:Color LaserJet Pro MFP M281fdw
                KyInfo() << "judge device add ";
                if (! devices.contains(str, Qt::CaseSensitive)) {
                    KyInfo() << "judge device add: " << str;

                    // current open device name will not search by 'scanimage -L',
                    // such as FUJITSU fi-7140
                    QString currentOpenDeviceName = g_sane_object->openSaneName;
                    if (currentOpenDeviceName == str) {
                        KyInfo() << "current open device name: " << currentOpenDeviceName;
                        continue;
                    }

                    if (! m_detectScanDevicesThread.isRunning()) {
                        g_sane_object->saneExit();
                        m_detectScanDevicesThread.start();

                    }

                    KyInfo() << "device add";
                    QString msg = tr("There is a new scanner connect, redetect all scanners, please wait a moment. ");
                    warnMsg(msg);

                    return;
                } else {
                    KyInfo() << "judge device add: " << str;
                }
            }
        }
    });

}

void MainWidget::usbRemovedOperationSlot(QString recvData)
{
    KyInfo() << "USB Remove: " << recvData;


    QProcess *scanList = new QProcess(this);
    QStringList  argvList;
    argvList.append("-L");
    scanList->start("scanimage", argvList);

    connect(scanList, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
    [ = ](int exitCode, QProcess::ExitStatus exitStatus) {

        KyInfo() << "USB Remove exitCode = " << exitCode
                 << "exitStatus = " << exitStatus;

        if (0 == exitCode) {
            QString result = QString::fromLocal8Bit(scanList->readAllStandardOutput());
            KyInfo() << "result = " << result;

            QStringList strListDevice;
            strListDevice =g_sane_object->getSaneNames();
            KyInfo() << "current sane names: " << strListDevice
                     << "userInfo.name= " << g_sane_object->userInfo.name
                     << "size = " << strListDevice.size();


            for (int i = 0; i < strListDevice.size(); ++i) {
                QString str = strListDevice.at(i).toLocal8Bit().constData();
                KyInfo() << "i=" << i << "str = " << str;

                if (str == tr("No available device")) {
                    break;
                }

                int warnFlag = 0;

                /// There are two cases that we cannot find scanners throught `scanimage -L`:
                /// case 1. no this scanner in system, which means this scanner has been disconnect
                /// case 2. this scanner has been connect in kylin-scanner by sane_init
                /// case 3. this scanner has been disconnet, but net or usb can be found CaseInsensitive, such as hp:Color LaserJet Pro MFP M281fdw
                if (! result.contains(str, Qt::CaseSensitive)) {
                    QString msg;
                    bool retStatus;
                    msg = tr("device ") + str + tr(" has been disconnect.");
                    KyInfo() << "usbRemoved msg: " << msg
                             << "userInfo.name = " << g_sane_object->userInfo.name
                             << "str = " << str;

                    if (g_sane_object->userInfo.name == str) {
                        KyInfo() << "The user choose device: " << str << "has been disconnect!";

                        // get argument again, because it cannot search using scanner while has opened it
                        if (m_scanThread.isRunning()) {
                            KyInfo() << "scanThread is running, so quit it.";
                            m_scanThread.quit();
                        }

                        KyInfo() << "begin to open sane device.";
                        g_sane_object->openSaneDeviceForPage(i);

                        QString currentOpenDeviceName = g_sane_object->openSaneName;
                        msg = tr("device ") + currentOpenDeviceName + tr(" has been disconnect.");
                        retStatus = g_sane_object->getSaneStatus();

                        KyInfo() << "test scanning end, status = " << retStatus;

                        if (strListDevice.size() == 1 && str.contains("Pantum DS-230", Qt::CaseSensitive)) {
                            // handle Pantum DS-230 scanners, which still could sane_open true while usb removed
                            KyInfo() << "handle Pantum DS-230 scanners.";
                            warnFlag = 1;
                        }

                        if (!retStatus) {
                            warnMsg(msg);

                            // Need this before detect devices again, because meet `Error during device I/O`,
                            // which could crash this application.

                            if (! m_detectScanDevicesThread.isRunning()) {
                                g_sane_object->saneExit();
                                m_detectScanDevicesThread.start();
                            }

                            if (result.contains("No scanners were identified", Qt::CaseInsensitive)) {
                                KyInfo() << "No scanners were identified.";
                                // If not find any scan device
//                                m_displayWidget->showFailedPageSlot();
                            }
                        } else {
                            KyInfo() << "sane_open is true";

                            if (warnFlag == 1) {
                                warnMsg(msg);

                                // Need this before detect devices again, because meet `Error during device I/O`,
                                // which could crash this application.

                                if (! m_detectScanDevicesThread.isRunning()) {
                                    g_sane_object->saneExit();
                                    m_detectScanDevicesThread.start();
                                }

                                if (result.contains("No scanners were identified", Qt::CaseInsensitive)) {
                                    KyInfo() << "No scanners were identified.";
                                    // If not find any scan device
                                    //                                m_displayWidget->showFailedPageSlot();
                                }

                            }

                        }

                    }
                }
            }
        }
    });

}

void MainWidget::startScanOperationSlot()
{
    g_sane_object->ocrFlag = 0;

    g_user_signal->exitWindowWithSaveFlag = false;

    isExited = false;
    g_sane_object->stopSaneRead(false);

    if (m_scanThread.isRunning()) {
        // complicate click scan button, will be crashed
        KyInfo() << "complicate click scan button.";
        return;
    }
    m_scanThread.start();

#if 0
    QString pageNumber = g_sane_object->userInfo.pageNumber;
    int retCompare = QString::compare(pageNumber, tr("Single"), Qt::CaseInsensitive);
    if (retCompare == 0 ) {
        return;
    }
#endif

    showScanDialogSlot();
}

void MainWidget::stopScanOperationSlot()
{
    isExited = true;
    g_sane_object->stopSaneRead(true);

    if (m_detectScanDevicesThread.isRunning()) {
        KyInfo() << "Quit window, so quit detect scan devices thread ...";
        // handle usb remove and click scan button crashed error: terminate called without an active exception
//        m_detectScanDevicesThread.terminate();
    }


}

void MainWidget::showScanDialogSlot()
{
    m_scanDialog = new ScanDialog();
//    m_scanDialog->setWindowModality(Qt::ApplicationModal);
//    m_scanDialog->setAttribute(Qt::WA_ShowModal, true);

    QPoint globalPos = this->mapToGlobal(QPoint(0, 0));
    m_scanDialog->move(globalPos.x() + (this->width() - m_scanDialog->width())/2,
                        globalPos.y() + (this->height() - m_scanDialog->height())/2);

    m_scanDialog->exec();
}
void MainWidget::scanThreadFinishedSlot(int saneStatus)
{
    KyInfo() << "saneStatus: " << saneStatus;

    if (saneStatus != SANE_STATUS_GOOD) {
        if (saneStatus == SANE_STATUS_INVAL) {
            warnMsg(tr("Invalid argument, please change arguments or switch other scanners."));
        } else if (saneStatus == SANE_STATUS_EOF) {
            g_sane_object->haveScanSuccessImage = true;

            m_displayWidget->showSuccessImageHandlePageSlot();
            if(!cancelFlag){
                g_user_signal->scanThreadFinishedImageLoad();
            }else{
                QString loadPath = g_sane_object->loadFullScanFileName;
                if(QFile::remove(loadPath)){
                    qDebug()<<"delete current image :" << loadPath <<" success!";
                }
                cancelFlag = false;
            }

        } else if (saneStatus == SANE_STATUS_DEVICE_BUSY) {
            warnMsg(tr("Device busy, please wait or switch other scanners."));
        } else if(saneStatus == SANE_STATUS_NO_DOCS) {
            warnMsg(tr("Document feeder out of documents, please place papers and scan again."));
        } else if(saneStatus == SANE_STATUS_CANCELLED) {
            warnMsg(tr("Scan operation has been cancelled."));
        } else {
            warnMsg(tr("Scan failed, please check your scanner or switch other scanners."));
        }

        isExited = true;
        g_user_signal->stopScanOperation();
    } else {
        g_sane_object->haveScanSuccessImage = true;

        m_displayWidget->showSuccessImageHandlePageSlot();
        if(!cancelFlag){
            g_user_signal->scanThreadFinishedImageLoad();
        }else{
            QString loadPath = g_sane_object->loadFullScanFileName;
            if(QFile::remove(loadPath)){
                qDebug()<<"delete current image :" << loadPath <<" success!";
            }
            cancelFlag = false;
        }
        // to do: update thumbtailwidget icon to current filepath

    }

}
void MainWidget::cancelScanningSlot(){
    cancelFlag = true;
}

void MainWidget::startOcrOperationSlot()
{
    m_ocrThread.start();

//    if (m_isOcrInit) {
//        return;
//    }
//    m_ocrObject =  new OcrObject();
//    m_ocrThread = new QThread();
//    m_ocrObject->moveToThread(m_ocrThread);
//    m_ocrThread->start();
//    m_isOcrInit = true;

}

void MainWidget::showBeautyRunningDialog()
{
    m_beautyRunningDialog = new RunningDialog(this, tr("Running beauty ..."));

    QPoint globalPos = this->mapToGlobal(QPoint(0, 0));
    int m_x = (this->width() - m_beautyRunningDialog->width()) / 2;
    int m_y = (this->height() - m_beautyRunningDialog->height()) / 2;

    m_beautyRunningDialog->move(globalPos.x() + m_x, globalPos.y() + m_y);

    m_beautyRunningDialog->exec();


//    g_user_signal->doBeautyOperation();
}

void MainWidget::startBeautyOperationSlot()
{
    m_beautyThread.start();

    showBeautyRunningDialog();
}

void MainWidget::hideBeautyRunningDialog()
{
    m_beautyRunningDialog->hide();
    m_beautyRunningDialog->reject();
}

void MainWidget::showRectifyRunningDialog()
{
    m_rectifyRunningDialog = new RunningDialog(this, tr("Running rectify ..."));

    QPoint globalPos = this->mapToGlobal(QPoint(0, 0));
    int m_x = (this->width() - m_rectifyRunningDialog->width()) / 2;
    int m_y = (this->height() - m_rectifyRunningDialog->height()) / 2;

    m_rectifyRunningDialog->move(globalPos.x() + m_x, globalPos.y() + m_y);

    m_rectifyRunningDialog->setModal(true);
    m_rectifyRunningDialog->show();

    connect(m_rectifyRunningDialog, &RunningDialog::rejected, this, &MainWidget::isRejectedRectifySlot);

    g_user_signal->doRectifyOperation();
}

void MainWidget::startRectifyOperationSlot()
{
//    QEventLoop eventLoop;
    m_rectifyThread.start();
//    connect(&m_rectifyThread, SIGNAL(finished()), &eventLoop, SLOT(quit()));
//    m_rectifyThread.wait();
    //    eventLoop.exec();
}

void MainWidget::isRejectedRectifySlot()
{
    KyInfo() << "rectify is reject.";
    if (! m_rectifyRunningDialog->isHidden()) {
        m_rectifyRunningDialog->hide();
        m_rectifyRunningDialog->reject();
    }
}

void MainWidget::hideRectifyRunningDialog(bool isTrue)
{
    KyInfo() << "hide rectify dialog";
    if (! m_rectifyRunningDialog->isHidden()) {
        m_rectifyRunningDialog->hide();
        m_rectifyRunningDialog->reject();
    }
}


void DetectScanDevicesThread::run()
{
    KyInfo() << "FindScanDevicesThread begin";

    g_sane_object->detectSaneDeviceForPage();

    if (! g_sane_object->getSaneStatus()) {
        emit detectScanDevicesFinishedSignal(false);
        KyInfo() << "detect scan devices finished: false.";
    } else {
        emit detectScanDevicesFinishedSignal(true);
        KyInfo() << "detect scan devices finished: true.";
    }
}


void ScanThread::run()
{
    int sleepTime = 0;
    int ret = 0;

    g_sane_object->scanPageNumber = 0;

    QString pageNumber = g_sane_object->userInfo.pageNumber;
    int retCompare = QString::compare(pageNumber, tr("Multiple"), Qt::CaseInsensitive);

    if (retCompare == 0) {
        sleepTime = getSleepTime(g_sane_object->userInfo.time);
        KyInfo() << "sleepTime = " << sleepTime << "多页扫描";
    }

    do {
        emit g_user_signal->dbusInhabitSignal();

        KyInfo() << "start_scanning start";
        ret = g_sane_object->startScanning(g_sane_object->userInfo);
        KyInfo() << "start_scanning end, status = " << ret;

        int saneStatus = ret;

        g_user_signal->scanThreadFinished(ret);

        emit g_user_signal->dbusUnInhabitSignal();
        if(isExited){
            emit g_user_signal->cancelOprationOKSignal();
            qDebug()<<"exit before delay!";
            break;
        }

        if (saneStatus != SANE_STATUS_GOOD) {
            // Avoid duplicate scan in multiple scanning while meet error
            break;
        }

        KyInfo() << "start sleep time: " << sleepTime;
        sleep(sleepTime);

        g_sane_object->scanPageNumber += 1;

        KyInfo() << "sleep end."
                 << "scanPageNumber = " << g_sane_object->scanPageNumber;

    } while ((sleepTime != 0) && (! isExited));
    emit g_user_signal->cancelOprationOKSignal();
}

void UsbHotplugThread::run()
{
        hotplug_sock = init_hotplug_sock();
        do {
            /* Netlink message buffer */
            char buf[UEVENT_BUFFER_SIZE * 2] = {0};
            recv(hotplug_sock, &buf, sizeof(buf), 0);


            QString recvData = QString(QLatin1String(buf));

#if 0
            if (! recvData.isEmpty()) {
                KyInfo() << "recvData: " << recvData;
            }
#endif

            if (recvData.contains("add@", Qt::CaseInsensitive)
                    && recvData.contains("devices", Qt::CaseInsensitive)
                    && recvData.contains("usb", Qt::CaseInsensitive)
                    && recvData.endsWith(":1.0", Qt::CaseInsensitive)) {
                // ":1.0" means each usb device first directory, which is must
                emit usbAdd(recvData);
            } else if (recvData.contains("remove@", Qt::CaseInsensitive)
                       && recvData.contains("devices", Qt::CaseInsensitive)
                       && recvData.contains("usb", Qt::CaseInsensitive)
                       && recvData.endsWith(":1.0", Qt::CaseInsensitive)) {
                emit usbRemove(recvData);
            }
        } while (! exitWindowFlag);
        quit();
}


int ScanThread::getSleepTime(QString &time)
{
    if (QString::compare(time, "3s", Qt::CaseInsensitive) == 0
            || QString::compare(time, tr("3s"), Qt::CaseInsensitive) == 0) {
        return 3;
    } else if (QString::compare(time, "5s", Qt::CaseInsensitive) == 0
               || QString::compare(time, tr("5s"), Qt::CaseInsensitive) == 0) {
        return 5;
    } else if (QString::compare(time, "7s", Qt::CaseInsensitive) == 0
               || QString::compare(time, tr("7s"), Qt::CaseInsensitive) == 0) {
        return 7;
    } else if (QString::compare(time, "10s", Qt::CaseInsensitive) == 0
               || QString::compare(time, tr("10s"), Qt::CaseInsensitive) == 0) {
        return 10;
    } else if (QString::compare(time, "15s", Qt::CaseInsensitive) == 0
               || QString::compare(time, tr("15s"), Qt::CaseInsensitive) == 0) {
        return 15;
    }
}

BeautyThread::BeautyThread(QObject *parent)
    : QThread(parent)
{
    KyInfo() << "Create beauty thread.";

}

BeautyThread::~BeautyThread()
{
}

void BeautyThread::run()
{
    KyInfo() << "begin to run beauty thread.";

    oneClickBeauty(ScanningPicture);

    KyInfo() << "end oneClickBeauty()";

    g_user_signal->doBeautyOperationFinished();

}

void BeautyThread::beautyThreadStop()
{
    KyInfo() << "beauty thread stop";
    if(isRunning())
    {
        terminate();
        wait();
    }
}


RectifyThread::RectifyThread(QObject *parent)
    : QThread(parent)
{
    KyInfo() << "Create rectify thread.";
}

RectifyThread::~RectifyThread()
{
}

void RectifyThread::run()
{
    KyInfo() << "begin to run rectify thread.";

    int res = 0;
    res = ImageRectify(ScanningPicture);

    KyInfo() << "end ImageRectify(), res = " << res;

    if (0 == res) {
        g_user_signal->doRectifyOperationFinished(true);
    } else {
        g_user_signal->doRectifyOperationFinished(false);
    }

}

void RectifyThread::rectifyThreadStop()
{
    if(isRunning())
    {
        terminate();
        wait();
    }
}


OcrThread::OcrThread(QObject *parent)
    : QThread(parent)
{
    KyInfo() << "Create OCR Thread.";
}

OcrThread::~OcrThread()
{
}

void OcrThread::run()
{
    KyInfo() << "begin to run ocr thread !\n";
    tesseract::TessBaseAPI  *api = new tesseract::TessBaseAPI();

    if (api->Init(NULL, "chi_sim")) {
        KyInfo() << "Could not initialize tesseract.\n";
        g_sane_object->ocrOutputText = tr("Unable to read text");

        terminate();
        wait();

        g_user_signal->toolbarOcrOperationFinished();
        return;
    }

    KyInfo() << "before pixRead.";
    Pix *image = pixRead(ScanningPicture);
    if (! image) {
        KyInfo() << "pixRead error!";
        g_sane_object->ocrOutputText = tr("Unable to read text");
        if (api)
            api->End();

        terminate();
        wait();

        g_user_signal->toolbarOcrOperationFinished();

        return;
    }
    if (image) {
        KyInfo() << "before setImage.";
        api->SetImage(image);
        g_sane_object->ocrOutputText = api->GetUTF8Text();
    }

    KyInfo() << "before destroy image.";
    if (api) {
        api->End();
    }
    if (image) {
        pixDestroy(&image);
    }

    g_user_signal->toolbarOcrOperationFinished();
}

void OcrThread::ocrThreadStop()
{
    if(isRunning())
    {
        terminate();
        wait();
    }
}
